import {
    AmbientLight,
    Camera,
    DirectionalLight,
    PerspectiveCamera,
    Raycaster,
    Scene,
    SRGBColorSpace, Vector2,
    WebGLRenderer
} from 'three';
import * as THREE from 'three';
import Events from './Events';
import {OrbitControls} from "three-stdlib";
import mitt, { type Emitter } from 'mitt';
import Stats from 'three/examples/jsm/libs/stats.module';
import {throttle} from "lodash";
import SkyBoxs from "@/modules/SkyBoxs";
export type Animate = {
    fun: (arg: any) => any,
    content: any,
}
export default class Viewer  {
    public  id: string;
    public scene!:Scene;
    public viewerDom!: HTMLElement;
    public renderer!: WebGLRenderer;
    public camera!: PerspectiveCamera;
    public controls!: OrbitControls;
    public emitter!: Emitter<any>;
    public mouse!: Vector2;
    public animateEventList: Animate[] = [];
    public raycaster!: Raycaster;
    public mouseEvent: MouseEvent | undefined;
    public isDestroy: boolean = false;
    public raycasterObjects: THREE.Object3D[] = [];
    public statsControls!: Stats;
    public skyboxs!: SkyBoxs;
    constructor(id: string) {
        this.id = id;
        this.initViewer()
    }
    public addAnimate(animate: Animate) {
        this.animateEventList.push(animate);
    }
    public addStats() {
        if (!this.statsControls) this.statsControls = new Stats();
        this.statsControls.dom.style.position = 'absolute';
        this.viewerDom.appendChild(this.statsControls.dom);

        // 添加到动画
        this.addAnimate({
            fun: this.statsUpdate,
            content: this.statsControls,
        });
    }
    /**注册鼠标事件监听 */
    public initRaycaster() {
        this.raycaster = new Raycaster();

        const initRaycasterEvent: Function = (eventName: keyof HTMLElementEventMap): void => {
            const funWrap = throttle(
                (event: any) => {
                    this.mouseEvent = event;
                    this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
                    this.mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
                    // @ts-expect-error
                    this.emitter.emit(Events[eventName].raycaster, this.getRaycasterIntersectObjects());
                },
                50
            );
            this.viewerDom.addEventListener(eventName, funWrap, false);
        };

        initRaycasterEvent('click');
        initRaycasterEvent('dblclick');
        initRaycasterEvent('mousemove');
    }
    public destroy() {
        this.scene.traverse((child: any) => {
            if (child.material) {
                child.material.dispose();
            }
            if (child.geometry) {
                child.geometry.dispose();
            }
            child = null;
        });
        this.renderer.forceContextLoss();
        this.renderer.dispose();
        this.scene.clear();

        this.isDestroy = true;
    }
    private statsUpdate(statsControls: any) {
        statsControls.update();
    }
    private initViewer() {
        this.emitter = mitt();
        this.initScene()
        this.initRenderer()
        this.initSkybox()
        this.initCamera()
        this.initLight()
        this.initControl()
        this.raycaster = new Raycaster();
        this.mouse = new Vector2();

        const animate = () => {
            if (this.isDestroy) return;
            requestAnimationFrame(animate);

            this.updateDom();
            this.readerDom();

            // 全局的公共动画函数，添加函数可同步执行
            this.animateEventList.forEach(event => {
                // event.fun && event.content && event.fun(event.content);
                if (event.fun && event.content) {
                    event.fun(event.content);
                }
            });
        };

        animate();
    }
    private initScene() {
        this.scene = new Scene()
    }
    private initRenderer() {
        this.viewerDom = document.getElementById(this.id) as HTMLElement;
        this.renderer = new WebGLRenderer({
            logarithmicDepthBuffer: true,
            antialias: true, // true/false表示是否开启反锯齿
            alpha: true, // true/false 表示是否可以设置背景色透明
            precision: 'mediump', // highp/mediump/lowp 表示着色精度选择
            premultipliedAlpha: true, // true/false 表示是否可以设置像素深度（用来度量图像的分辨率）
        });
        this.renderer.clearDepth();

        this.renderer.shadowMap.enabled = true;
        this.renderer.outputColorSpace = SRGBColorSpace; // 可以看到更亮的材质，同时这也影响到环境贴图。
        this.viewerDom.appendChild(this.renderer.domElement);
    }
    private initCamera() {
        this.camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
        this.camera.position.set(4, 2, -3);
        this.camera.lookAt(0, 0, 0);
    }
    public updateCamera(){
        this.controls.update();
    }
    private initLight(){
        const ambient = new AmbientLight(0xffffff, 0.6);
        this.scene.add(ambient);
        const light = new DirectionalLight(0xffffff, 1);
        light.position.set( 0, 200, 100 );
        light.castShadow = true;

        light.shadow.camera.top = 180;
        light.shadow.camera.bottom = -100;
        light.shadow.camera.left = -120;
        light.shadow.camera.right = 400;
        light.shadow.camera.near = 0.1;
        light.shadow.camera.far = 400;
        // 设置mapSize属性可以使阴影更清晰，不那么模糊
        light.shadow.mapSize.set(1024, 1024);

        this.scene.add(light);
    }
    private initSkybox() {
        if (!this.skyboxs) this.skyboxs = new SkyBoxs(this);
        this.skyboxs.addSkybox('night');
        this.skyboxs.addFog();
    }
    private initControl() {
        this.controls = new OrbitControls(
            this.camera as Camera,
            this.renderer?.domElement
        );
        this.controls.enableDamping = false;
        this.controls.screenSpacePanning = false; // 定义平移时如何平移相机的位置 控制不上下移动
        this.controls.minDistance = 2;
        this.controls.maxDistance = 1000;
        this.controls.addEventListener('change', ()=>{
            this.renderer.render(this.scene, this.camera);
        });
    }
    private readerDom() {
        this.renderer?.render(this.scene as Scene, this.camera as Camera);
    }

    private updateDom() {
        this.controls.update();
        // 更新参数
        this.camera.aspect = this.viewerDom.clientWidth / this.viewerDom.clientHeight; // 摄像机视锥体的长宽比，通常是使用画布的宽/画布的高
        this.camera.updateProjectionMatrix(); // 更新摄像机投影矩阵。在任何参数被改变以后必须被调用,来使得这些改变生效
        this.renderer.setSize(this.viewerDom.clientWidth, this.viewerDom.clientHeight);
        this.renderer.setPixelRatio(window.devicePixelRatio); // 设置设备像素比
    }
    /**自定义鼠标事件触发的范围，给定一个模型组，对给定的模型组鼠标事件才生效 */
    public setRaycasterObjects (objList: THREE.Object3D[]): void {
        this.raycasterObjects = objList;
    }

    private getRaycasterIntersectObjects(): THREE.Intersection[] {
        if (!this.raycasterObjects.length) return [];
        this.raycaster.setFromCamera(this.mouse, this.camera);
        return this.raycaster.intersectObjects(this.raycasterObjects, true);
    }
}
